package zcache

import (
	"fmt"
	"log"
	"sync"
)

// 负责与外部交互, 控制缓存存储和获取的主流程


// MyCahce中不应该支持数据源的配置:
// 1. 数据源种类太多, 没办法一一实现
// 2. 扩展性不好
type Getter interface {
	// 定义获取原始数据的接口
	// 如果获取数据由用户自己决定
	Get(key string) ([]byte, error)
}


type GetterFunc func(key string) ([]byte, error)


func (f GetterFunc) Get(key string) ([]byte, error) {
	return f(key)
}


type Group struct {
	// 组名称, 唯一标识
	name string
	// 获取源数据的回调
	getter Getter
	mainCache cache
	peers PeerPicker
}


var (
	// 操作groups时的读写锁
	rw sync.RWMutex
	// 所有组
	groups = make(map[string]*Group)
)


// 创建一个新的Group, 可以把Group认为是缓存的命名空间
// Group名称必须唯一
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
	if getter == nil {
		panic("getter is nil")
	}

	rw.Lock()
	defer rw.Unlock()

	if _, ok := groups[name]; ok {
		panic("group name is exists")
	}

	g := &Group{
		// 组名称
		name: name,
		// 获取源数据的回调
		getter: getter,
		// cache
		mainCache: cache{cacheBytes: cacheBytes},
	}

	groups[name] = g
	return g
}


// 通过名称获取Group
func GetGroup(name string) *Group {
	rw.RLock()
	defer rw.RUnlock()
	g := groups[name]
	return g
}


func (g *Group) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}

	// 从缓存中获取数据
	if v, ok := g.mainCache.get(key); ok {
		log.Println("[ + zcache ] cache hit")
		return v, nil
	}

	// 从源加载数据
	return g.load(key)
}


// 从源加载数据
func (g *Group) load(key string) (value ByteView, err error) {
	// 暂时从本地取
	// 分布式场景下, 将尝试从其他节点中获取
	// return g.getLocally(key)
	if g.peers != nil {
		if peer, ok := g.peers.PickPeer(key); ok {
			if value, err := g.getFromPeer(peer, key); err == nil {
				return value, nil
			}
			log.Println("[ - zcache ] failed to get from peer", err)
		}
	}

	return g.getLocally(key)
}


// 加载数据
func (g *Group) getLocally(key string) (ByteView, error) {
	// 通过Get接口获取元数据
	bytes, err := g.getter.Get(key)
	if err != nil {
		return ByteView{}, err
	}

	// 转换格式后, 将其添加至缓存中
	value := ByteView{b: cloneBytes(bytes)}
	g.populateCache(key, value)
	return value, nil
}


// 将数据添加到缓存中
func (g *Group) populateCache(key string, value ByteView) {
	g.mainCache.add(key, value)
}


func (g *Group) RegisterPeers(peers PeerPicker) {
	if g.peers != nil {
		panic("RegisterPeerPicker called more than once")
	}
	g.peers = peers
}


func (g *Group) getFromPeer(peer PeerGetter, key string) (value ByteView, err error){
	bytes, err := peer.Get(g.name, key)
	if err != nil {
		return ByteView{}, err
	}

	return ByteView{b:bytes}, nil
}